/*!
 * A copy of this code can be obtained from:
 * http://code.google.com/p/core-js/
 *
 * The MIT License
 *
 * Copyright (c) 2009 Jyrki Laurila
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
;(function() {
    /* Initializing scope variables, to speed up references */
    var win = this, doc = win.document, ns = "core", core, config;
    
    /**
     * The namespace within window-object where the core will be created
     *
     * @example
     * // in global scope
     * CORE_NS = "myCore";
     *
     * @example
     * // in a private scope
     * window.CORE_NS = "myCore";
     *
     * @name CORE_NS
     * @constant
     * @default "core"
     */
    if (typeof(win.CORE_NS) == "string") {
        ns = win.CORE_NS;
    } else {
        win.CORE_NS = ns;
    }
    
    if (typeof(win[ns]) != "object") {
        win[ns] = {};
    }
    
    /**
     * The root namespace for the core-js framework
     *
     * @version $v0.9.4 2009-09-25
     * @namespace The root namespace for the core-js framework
     * @author Jyrki Laurila
     * @name core
     */
    core = win[ns];
    
    /* Check if the core has already been initialized so we won't overwrite anything */
    if (!core.init) {
        config = {
            version: "0.9.4",
            separator: "/",
            regMatch: /core\.js(\W|$)/i
        };
        
        /**
         * Returns a script src if it matches to given regexp pattern
         *
         * @example
         * var loadPath = core.getPath(/myfile\.js/i);
         *
         * @example
         * var loadPath = core.getPath("myfile\.js", "i");
         *
         * @name core.getPath
         * @methodOf core
         * @param {regexp|string} [reg] A regular expression to match. If not defined, tries to find core's load path.
         * @param {string} [opt] Optional regexp modifiers
         * @returns {string} The first matched src, without the matched part. Returns false if no matches.
         */
        function getPath(reg, opt) {
            reg = (typeof(reg) == "string" ? new RegExp(reg, opt) : reg) || config.regMatch;
            var s = doc.getElementsByTagName("script"), i = 0, len = s.length, src, chk;
            for (; i < len; i++) {
                src = s[i].src
                chk = (src ? src.match(reg) : null);
                if (chk) {
                    return src.substring(0, chk.index);
                }
            }
            return false;
        }
        
        config.path = getPath();
        
        /**
         * Extends an object with another object. If no second parameter is defined, we assume to be extending "this"
         *
         * @example
         * var myProperties = {
         *     foo: "bar",
         *     color: "red",
         *     shape: "circle"
         * };
         *
         * var myObject = {
         *     object: "balloon"
         * };
         *
         * core.extend(myObject, myProperties);
         *
         * alert(myObject.color); // will alert "red"
         *
         * @name core.extend
         * @methodOf core
         * @param {boolean|object} arg If boolean, we assume we want to make a deep copy, otherwise this is the extendee target.
         * @param {arguments} [1..n] The object we extend the target with.
         * @returns {object} Extended object
         */
        function extend() {
            var arg = arguments, target = (arg[0] || {}), i = 1, len = arg.length, deep = false, opt, name, src, copy;
            
            if (typeof(target) === "boolean") {
                deep = target;
                target = (arg[1] || {});
                i = 2;
            }
            
            if (typeof(target) != "object" && typeof(target) != "function") {
                target = {};
            }
            
            if (len == i) {
                target = this;
                i--;
            }
            
            for (; i < len; i++) {
                opt = arg[i];
                if (opt != null) {
                    for (name in opt) {
                        src = target[name];
                        copy = opt[name];
                        
                        if (target === copy) {
                            continue;
                        }
                        
                        if (deep && copy && typeof(copy) === "object" && !copy.nodeType) {
                            target[name] = extend(deep, src || (copy.length != null ? [] : {}), copy);
                        } else if (copy !== undefined) {
                            target[name] = copy;
                        }
                    }
                }
            }
            return target;
        }
        
        /**
         * Generates a namespace from a path and returns it
         *
         * @example
         * var myNameSpace = core.namespace("my.namespace");
         *
         * @example
         * var myObj = {
         *     doStuff: function() {
         *         alert("foo");
         *     }
         * };
         * 
         * core.namespace("my.namespace", myObj);
         * 
         * my.namespace.doStuff();
         *
         * @name core.namespace
         * @methodOf core
         * @param {string} path A dot separated path of the namespace
         * @param {mixed} [obj] The value that will be set to the target property described in path. Can be anything.
         * @param {boolean} [ext] If true, the end object will be an extendee for the target object. Defaults to false, which replaces the target object.
         * @returns {object} The generated namespace
         */
        function namespace(path, obj, ext) {
            var root = core, cur, src = path.split("."), len = src.length, i = 0;
            for (; i < len; i++) {
                cur = (root[src[i]] || {});
                if (i == (len - 1)) {
                    cur = (ext === true ? extend(cur, (obj || {})) : obj);
                }
                root = root[src[i]] = cur;
            }

            return root;
        }
        
        /**
         * Includes an external script or style from a specific location based on the name and/or the config path
         *
         * @example
         * core.include("core.lang");
         *
         * @example
         * core.include("lang", {
         *     path: "../src/core",
         *     callback: function() {
         *         alert("Script '" + script.name + "' loaded successfully");
         *     }
         * });
         * 
         * @example
         * core.include("http://my.url.com/path/to/script.js");
         *
         * @name core.include
         * @methodOf core
         * @param {string} name
         * @param {object} [cfg] Configuration object
         * @param {boolean} [cfg.dot] Preserve dots in the name, otherwise treats them as folder path (default)
         * @param {string} [cfg.path] Path where we should try to load the script from. Default is relative to core.
         * @param {string} [cfg.type] Type of the file to be loaded. "style" or "script" (default)
         * @param {string} [cfg.ext] Extension of the file to be loaded. Default is "js"
         * @param {string} [cfg.charset] Charset of the script to be included. Defaults to "UTF-8"
         * @param {function} [cfg.callback] The function to call after the import is done (works with type "script")
         * @param {object} [cfg.scope] Scope of the callback (the "this" object). Default is null (=global object).
         */
        function include(name, cfg) {
            var n, el, c = extend({
                dot: false,
                path: config.path,
                type: "script",
                ext: "js",
                charset: "UTF-8",
                scope: null
            }, (cfg || {})),
            url = (/^http(s)?\:\/\//i.test(name));
            
            if (!c.dot && !url) {
                n = name.split(".");
                name = n.join(config.separator);
            }
            
            var src = (url ? "" : c.path) + name + (c.ext && !url ? "." + c.ext : ""),
                ready = false,
                head = doc.getElementsByTagName("head")[0];
            
            if (getPath(src) === false) {
                if (c.type == "style") {
                    el = doc.createElement("link");
                    el.rel = "stylesheet";
                    el.type = "text/css";
                    el.href = src;
                } else {
                    el = doc.createElement("script");
                    el.type = "text/javascript";
                    el.charset = c.charset;
                    el.src = src;
                    if (typeof(c.callback) == "function") {
                        el.onload = el.onreadystatechange = function() {
                            if (!ready && (!this.readyState || this.readyState == 'complete' || this.readyState == 'loaded')) {
                                ready = true;
                                el.onload = el.onreadystatechange = null;
                                head.removeChild(el);
                                c.callback.call(c.scope);
                            }
                        }
                    }
                }
                
                head.appendChild(el);
                return true;
            }
            return false;
        }
        
        extend(core, {
            extend: extend,
            namespace: namespace,
            include: include,
            getPath: getPath,
            _config: config
        });
        
        core.init = true;
    }
})();
